﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using UnityEngine;


namespace Miao
{
    /// <summary>
    ///  The runtime for Unity.
    /// </summary>
    public static class MiaoRuntime
    {
//#if UNITY_EDITOR
        public static Updater Updater
        {
            get
            {
                if (_updater == null)
                {
                    root = new GameObject("MiaoRoot", typeof(Updater));
                    _updater = root.GetComponent<Updater>();
                    GameObject.DontDestroyOnLoad(root);
                }
                return _updater;
            }
            set
            {
                _updater = value;
            }
        }

        /// <summary>
        /// Called when runtime initialization completed.
        /// </summary>
        public static event Action onInitialized;
        public static Task initializationTask => GetTask();
        public static bool initialized { get; private set; }

        private static Updater _updater;
        private static GameObject root;
        private static readonly ModuleManager moduleManager = new ModuleManager();

        private static bool initializing;
        private static TaskCompletionSource<bool> taskCompletionSource;


//#else
//    public static Updater Updater;
//#endif
        static MiaoRuntime()
        {
            Main();
        }
        //[RuntimeInitializeOnLoadMethod]
        private static void Main()
        {
            if (!Application.isEditor)
            {
                root = new GameObject("MiaoRoot", typeof(Updater));
                Updater = root.GetComponent<Updater>();
                GameObject.DontDestroyOnLoad(root);
            }
        }

        public static async Task Initialize()
        {
            if (initialized || initializing) return;
            initializing = true;

            Type moduleBaseType = typeof(IModule);
            SortedDictionary<int, List<Type>> moduleDictionary = new SortedDictionary<int, List<Type>>();
            SortedDictionary<int, List<MethodInfo>> methodDictionary = new SortedDictionary<int, List<MethodInfo>>();

            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in assembly.GetTypes())
                {
                    foreach (var staticMethodInfo in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
                    {
                        RuntimeInitializeMethodAttribute runtimeInitializeMethod = staticMethodInfo.GetCustomAttribute<RuntimeInitializeMethodAttribute>();
                        if (runtimeInitializeMethod != null)
                        {
                            if (!methodDictionary.TryGetValue(runtimeInitializeMethod.priority, out List<MethodInfo> methodList))
                            {
                                methodDictionary.Add(runtimeInitializeMethod.priority, methodList = new List<MethodInfo>());
                            }
                            methodList.Add(staticMethodInfo);
                        }
                    }
                    if (type.IsAbstract || !moduleBaseType.IsAssignableFrom(type)) continue;
                    //没有无参构造函数, 报错
                    if (type.GetConstructor(Type.EmptyTypes) == null) throw new Exception($"{nameof(MiaoRuntime)}: Module \"{type.Name}\" 必须具备无参构造函数！");
                    ModuleInitializationPriorityAttribute moduleInitializationPriority = type.GetCustomAttribute<ModuleInitializationPriorityAttribute>();
                    int priority = moduleInitializationPriority == null ? 0 : moduleInitializationPriority.priority;
                    if (!moduleDictionary.TryGetValue(priority, out List<Type> list))
                    {
                        moduleDictionary.Add(priority, list = new List<Type>());
                    }
                    list.Add(type);
                }
            }
            //先初始化模块
            moduleManager.EnsureCapacity(moduleDictionary.Count);
            foreach (var moduleTypeList in moduleDictionary.Values)
            {
                await Task.WhenAll(moduleTypeList.Select(moduleType =>
                {
                    IModule module = (IModule)Activator.CreateInstance(moduleType);
                    moduleManager.AddModule(module);
                    return (module is Module moduleAsync) ? moduleAsync.Initialize() : Task.CompletedTask;
                }));
            }
            //再初始化方法
            foreach (var methodList in methodDictionary.Values)
            {
                await Task.WhenAll(methodList.Select(method => (method.Invoke(null, null) is Task task) ? task : Task.CompletedTask));
            }

            initialized = true;
            initializing = false;
            taskCompletionSource?.SetResult(true);
            onInitialized?.Invoke();
        }

        /// <summary>
        /// Get module of type <typeparamref name="T"/>.
        /// </summary>
        public static T GetModule<T>() where T : IModule
        {
            return moduleManager.GetModule<T>();
        }



        private static Task GetTask()
        {
            if (taskCompletionSource == null)
            {
                taskCompletionSource = new TaskCompletionSource<bool>();
                if (initialized) taskCompletionSource.SetResult(true);
            }
            return taskCompletionSource.Task;
        }
    }
}

